---
title: Key-Value Store API
description: API endpoints for the Terrateam key-value storage system
---

The KV Store provides persistent key-value storage for Terrateam installations. These endpoints allow you to store, retrieve, and manage data across workflow runs.

## Set Value

Store or update a key-value pair.

**Endpoint:** `PUT /api/v1/{vcs}/kv/{installation_id}/key/{key}`

**Path Parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `vcs` | string | Yes | VCS provider: `github` or `gitlab` |
| `installation_id` | string | Yes | The installation identifier |
| `key` | string | Yes | The key name |

**Request Body:**

Content-Type: `application/json`

Schema: `kv-set`

```json
{
  "data": "string",
  "idx": 0,
  "committed": false,
  "read_caps": [],
  "write_caps": []
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `data` | string | Yes | The value to store (JSON stringified if needed) |
| `idx` | integer | No | Index for versioning (default: 0) |
| `committed` | boolean | No | Whether to commit the value immediately |
| `read_caps` | array | No | Array of read capability restrictions for this key |
| `write_caps` | array | No | Array of write capability restrictions for this key |

**Responses:**

- **200**: Success - Returns the stored record
- **403**: Forbidden

**Response Schema (200):**

Schema: `kv-record`

```json
{
  "key": "string",
  "data": "string",
  "idx": 0,
  "version": 1,
  "committed": true,
  "created_at": "2025-01-15T10:30:00Z",
  "size": 1024,
  "read_caps": [],
  "write_caps": []
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `key` | string | Yes | The key name |
| `data` | string | Yes | The stored value |
| `idx` | integer | Yes | Index version |
| `version` | integer | Yes | Record version for optimistic locking |
| `committed` | boolean | Yes | Whether the record is committed |
| `created_at` | string | Yes | ISO 8601 timestamp of when the record was created |
| `size` | integer | Yes | Size of the stored data in bytes |
| `read_caps` | array | No | Array of read capability restrictions for this key |
| `write_caps` | array | No | Array of write capability restrictions for this key |

**Example Request:**

```bash
curl -X PUT \
  https://app.terrateam.io/api/v1/github/kv/{installation_id}/key/my-key \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"data": "my-value", "idx": 0}'
```

---

## Get Value

Retrieve a value by key.

**Endpoint:** `GET /api/v1/{vcs}/kv/{installation_id}/key/{key}`

**Path Parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `vcs` | string | Yes | VCS provider: `github` or `gitlab` |
| `installation_id` | string | Yes | The installation identifier |
| `key` | string | Yes | The key name |

**Query Parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `committed` | boolean | No | Only return committed values |
| `idx` | integer | No | Specific index to retrieve |
| `select` | array[string] | No | Fields to select from the record |

**Responses:**

- **200**: Success - Returns the record
- **403**: Forbidden
- **404**: Not Found

**Response Schema (200):**

Schema: `kv-record`

**Example Request:**

```bash
curl -X GET \
  "https://app.terrateam.io/api/v1/github/kv/{installation_id}/key/my-key?committed=true" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

---

## Delete Value

Delete a key-value pair.

**Endpoint:** `DELETE /api/v1/{vcs}/kv/{installation_id}/key/{key}`

**Path Parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `vcs` | string | Yes | VCS provider: `github` or `gitlab` |
| `installation_id` | string | Yes | The installation identifier |
| `key` | string | Yes | The key name |

**Query Parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `idx` | integer | No | Specific index to delete |
| `version` | integer | No | Version number for optimistic locking |

**Responses:**

- **200**: Success
- **403**: Forbidden

**Response Schema (200):**

Schema: `kv-delete`

```json
{
  "result": true
}
```

| Field | Type | Description |
|-------|------|-------------|
| `result` | boolean | Whether the key was successfully deleted |

---

## Compare-and-Swap (CAS)

Atomically update a value only if it matches the expected version.

**Endpoint:** `PUT /api/v1/{vcs}/kv/{installation_id}/cas/key/{key}`

**Path Parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `vcs` | string | Yes | VCS provider: `github` or `gitlab` |
| `installation_id` | string | Yes | The installation identifier |
| `key` | string | Yes | The key name |

**Request Body:**

Content-Type: `application/json`

Schema: `kv-cas`

```json
{
  "data": "string",
  "idx": 0,
  "version": 1,
  "committed": false,
  "read_caps": [],
  "write_caps": []
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `data` | string | Yes | The new value to store |
| `idx` | integer | No | Index for versioning |
| `version` | integer | Yes | Expected current version (CAS will fail if version doesn't match) |
| `committed` | boolean | No | Whether to commit the value immediately |
| `read_caps` | array | No | Array of read capability restrictions for this key |
| `write_caps` | array | No | Array of write capability restrictions for this key |

**Responses:**

- **200**: Success - Returns the updated record
- **400**: Bad Request - CAS condition failed (version mismatch)
- **403**: Forbidden

**Response Schema (200):**

Returns a `kv-record` (see Set Value endpoint for schema details)

**Example Request:**

```bash
curl -X PUT \
  https://app.terrateam.io/api/v1/github/kv/{installation_id}/cas/key/my-key \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"data": "new-value", "version": 1}'
```

---

## Iterate Keys

Retrieve multiple keys matching a pattern or prefix.

**Endpoint:** `GET /api/v1/{vcs}/kv/{installation_id}/iter/{key}`

**Path Parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `vcs` | string | Yes | VCS provider: `github` or `gitlab` |
| `installation_id` | string | Yes | The installation identifier |
| `key` | string | Yes | The key or key prefix |

**Query Parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `committed` | boolean | No | Only return committed values |
| `idx` | integer | No | Starting index for iteration |
| `limit` | integer | No | Maximum number of records to return |
| `include_data` | boolean | No | Include record data in response |
| `inclusive` | boolean | No | Include the starting key in results |
| `prefix` | boolean | No | Treat key as prefix for matching |
| `select` | array[string] | No | Fields to select from records |

**Responses:**

- **200**: Success - Returns list of records
- **403**: Forbidden

**Response Schema (200):**

Schema: `kv-record-list`

```json
{
  "records": [
    {
      "key": "string",
      "data": "string",
      "idx": 0,
      "version": 1,
      "committed": true
    }
  ]
}
```

| Field | Type | Description |
|-------|------|-------------|
| `records` | array | Array of `kv-record` objects (see Set Value for record schema) |

**Example Request:**

```bash
curl -X GET \
  "https://app.terrateam.io/api/v1/github/kv/{installation_id}/iter/my-prefix?prefix=true&limit=100" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

---

## Count Keys

Count the number of keys matching a pattern or prefix.

**Endpoint:** `GET /api/v1/{vcs}/kv/{installation_id}/count/key/{key}`

**Path Parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `vcs` | string | Yes | VCS provider: `github` or `gitlab` |
| `installation_id` | string | Yes | The installation identifier |
| `key` | string | Yes | The key or key prefix |

**Query Parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `committed` | boolean | No | Only count committed values |

**Responses:**

- **200**: Success - Returns the count
- **403**: Forbidden

**Response Schema (200):**

Schema: `kv-count`

```json
{
  "count": 42,
  "max_idx": 5
}
```

| Field | Type | Description |
|-------|------|-------------|
| `count` | integer | Number of keys matching the query |
| `max_idx` | integer | Maximum index value among the counted keys |

---

## Get Size

Get the storage size of a key or key prefix.

**Endpoint:** `GET /api/v1/{vcs}/kv/{installation_id}/size/key/{key}`

**Path Parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `vcs` | string | Yes | VCS provider: `github` or `gitlab` |
| `installation_id` | string | Yes | The installation identifier |
| `key` | string | Yes | The key or key prefix |

**Query Parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `committed` | boolean | No | Only measure committed values |
| `idx` | integer | No | Specific index to measure |

**Responses:**

- **200**: Success - Returns the size
- **403**: Forbidden
- **404**: Not Found

**Response Schema (200):**

Schema: `kv-size`

```json
{
  "size": 1024
}
```

| Field | Type | Description |
|-------|------|-------------|
| `size` | integer | Size in bytes of the stored data |

---

## Commit Transaction

Commit a batch of KV operations atomically.

**Endpoint:** `POST /api/v1/{vcs}/kv/{installation_id}/commit`

**Path Parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `vcs` | string | Yes | VCS provider: `github` or `gitlab` |
| `installation_id` | string | Yes | The installation identifier |

**Request Body:**

Content-Type: `application/json`

Schema: `kv-commit`

```json
{
  "keys": [
    {
      "key": "string",
      "idx": 0
    }
  ]
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `keys` | array | Yes | Array of key objects to commit |
| `keys[].key` | string | Yes | The key name to commit |
| `keys[].idx` | integer | No | Optional index for the key |

**Responses:**

- **200**: Success - Returns commit result
- **403**: Forbidden

**Response Schema (200):**

Schema: `kv-commit-result`

```json
{
  "keys": [
    {
      "key": "string",
      "idx": 0
    }
  ]
}
```

| Field | Type | Description |
|-------|------|-------------|
| `keys` | array | Array of committed key objects |
| `keys[].key` | string | The key name that was committed |
| `keys[].idx` | integer | The index that was committed |

**Example Request:**

```bash
curl -X POST \
  https://app.terrateam.io/api/v1/github/kv/{installation_id}/commit \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keys": [{"key": "my-key", "idx": 0}]}'
```

---

## Use Cases

The KV Store is useful for:

- Sharing state between workflow runs
- Storing metadata and configuration
- Implementing counters and locks
- Caching expensive computations
- Coordinating across multiple pull requests

:::tip
Use the `committed` parameter to ensure you're reading stable, committed data in production workflows.
:::

## Authentication

All endpoints require authentication using a short-lived access token:

```
Authorization: Bearer YOUR_ACCESS_TOKEN
```

**To get an access token:**

1. Create an API key in **Settings > API Access** in the Terrateam dashboard
2. Call `POST /api/v1/access-token/refresh` with your API key to get an access token
3. Use the access token for API requests

See the [Authentication guide](/reference/api/authentication) for detailed instructions.
